/*
 * DistanceForm.java
 * An application to log where you go, how long it took you and how far you went.
 *
 * Created on 18 mai 2006, 22:45
 *
 */

package courselog;

import courselog.gpsutils.GeoPoint;
import courselog.gpsutils.TrackPoint;
import courselog.gpsutils.TrackRecord;
import courselog.gpsutils.XPositionDisplayer;
import ewe.fx.IconAndText;
import ewe.sys.mThread;
import ewe.ui.Control;
import ewe.ui.ControlEvent;
import ewe.ui.CellPanel;
import ewe.ui.Event;
import ewe.fx.Color;
import ewe.fx.Dimension;
import ewe.fx.Font;
import ewe.sys.Device;
import ewe.sys.Vm;
import ewe.ui.Form;
import ewe.ui.Gui;
import ewe.ui.Menu;
import ewe.ui.MenuEvent;
import ewe.ui.MenuItem;
import ewe.ui.mLabel;
import courselog.gpsutils.Converters;
import courselog.gpsutils.PositionTracer;
import courselog.gpsutils.SerialGPSConnection;

/**
 * Panel for displaying the current trace status.
 * @author CourseLog
 * @version 2.0 Release with maps.
 */
public class DistanceForm extends CourseLogAppForm implements XPositionDisplayer {
    
    /** Record Item. */
    protected IconAndText iconRec= new IconAndText("Record","res/record.png",
            Color.White,null);
    
    /** Stop Item. */
    protected IconAndText iconStop= new IconAndText("Stop","res/stop.png",
            Color.White,null);
    
    /** Hold Item */
    protected IconAndText iconHold=new IconAndText("Hold","res/hold.png",
            Color.White,null);
    
    /** Pause Item */
    protected IconAndText iconPause=new IconAndText("Pause","res/pause.png",
            Color.White,null);
    
    /**
     * GPS button.
     */
    protected ActionKeySensitiveButton bGps = new ActionKeySensitiveButton(
            "GPS","res/SatGPS.png",null);
    
    /** Button for application navigation */
    protected PullDownForKey bGoto;
    
    protected PullDownForKey pdRecord;
    
    /** Record or Stop menu item. */
    MenuItem miRecStop=new MenuItem("Record$R","res/record.png",Color.White);
    
    /** Hold menu item */
    MenuItem miHold=new MenuItem("Hold$H","res/hold.png",Color.White);
    
    /** Pause menuItem */
    MenuItem miPause=new MenuItem("Pause$P","res/pause.png",Color.White);
    
    /**
     * Label that displays the content of the UTC value.
     */
    protected mLabel lUTCc=new mLabel("00:00:00");
    
    /**
     * Label that displays the content of the GPS latitude value.
     */
    protected mLabel lLatc=new mLabel("00°00'00.000N");
    
    /**
     * Label that displays the content of the GPS longitude value.
     */
    protected mLabel lLonc=new mLabel("000°00'00.000E");
    
    /**
     * Label that displays the content of the current point altitude value.
     */
    protected mLabel lAltc=new mLabel(" 0000.0 m");
    
    /**
     * Label that displays the content of the current point instantaneous
     * speed value.
     */
    protected mLabel lSpeedc=new mLabel("000.0 km/h");
    
    /**
     * Label that displays the content of the current point instantaneous
     * heading value.
     */
    protected mLabel lHeadingc=new mLabel("000.0°");
    
    /**
     * Label that displays the content of the current point elapsed time
     * value since the start of the trace.
     */
    protected mLabel lTimec= new mLabel("000h00'00\"");
    
    /** Label that displays the status */
    protected ActionKeySensitiveButton bStatusc=new ActionKeySensitiveButton(
            "xxxxxxxxxxxx","res/pause.png",Color.White);
    
    /**
     * Label that displays the content of the total integrated distance
     * value since the start of the trace.
     */
    protected mLabel lLenc= new mLabel(" 00000.000 km");
    
    /**
     * Label that displays the content of the total upward displacement
     * value since the start of the trace.
     */
    protected mLabel lUpc=new mLabel(" 0000.0 m");
    
    /**
     * Label that displays the content of the total downward displacement
     * value since the start of the trace.
     */
    protected mLabel lDwc=new mLabel(" 0000.0 m");
    
    /**
     * Label that displays the content of the current point average
     * speed value.
     */
    protected mLabel lAvSpeedc=new mLabel("000.0 km/h");
    
    /**
     * Label that displays the content of the current point average
     * heading value.
     */
    protected mLabel lAvHeadingc=new mLabel("000.0°");
    
    // State fields
    
    /** My calling application. */
    MainApplication myApp;
    
    /** Is this panel active or not. */
    protected boolean myIsActive=false;
    
    /** Last displayed GPS status */
    protected int myLastGPSStatus=SerialGPSConnection.STATUS_NOK;
    
    /** Last tracer identified */
    protected PositionTracer myLastTracer=null;
    
    /** Last tracer state*/
    protected int myLastTracerState=PositionTracer.STATUS_OFF;
    
    // CONSTRUCTORS
    
    /**
     * Creates a new instance of DistanceForm. Called by ewe at runtime.
     * This initialises all application's GUI with fonts and sizes of each
     * panel.
     * Also defines the options and starts looking for the GPS receiver.
     * @param mainApp Main application using this object.
     */
    public DistanceForm(MainApplication mainApp) {
        
        myApp=mainApp;
        setPreferredSize(240, 320);
        
        String strTitle="Distance - CourseLog ";
        if( SerialGPSConnection.isLogging ) {
            strTitle += " Logging";
        }
        if( SerialGPSConnection.isEmulating) {
            strTitle += " Emulating";
        }
        setTitle(strTitle);
        
        Form.globalIcon = myApp.myAppGlobalIcon;
        Dimension dScr=Device.getScreenSize();
        
        bGps.backGround=MainApplication.BADCOLOR;
        bGps.setPreferredSize(dScr.width/32, dScr.height/16);
        
        mLabel lUTCt=new mLabel("UTC:");
        Font normalFont = lUTCt.getFont();
        
        int nHeight = normalFont.getSize();
        Font largeFont = normalFont.changeNameAndSize(null, nHeight+12);
        largeFont = largeFont.changeStyle(Font.BOLD);
        Font mediumFont = normalFont.changeNameAndSize(null, nHeight+6);
        normalFont = normalFont.changeNameAndSize(null, nHeight+2);
        Font boldFont = mediumFont.changeStyle(Font.BOLD);
        
        lUTCc.backGround = Color.White;
        lUTCc.setFont(normalFont);
        mLabel lLatt=new mLabel("Lat:");
        lLatt.setFont(normalFont);
        lLatc.setFont(normalFont);
        //lLatc.setBorder(BDR_OUTLINE|BF_RECT,1);
        mLabel lLont=new mLabel("Lon:");
        lLont.setFont(normalFont);
        lLonc.setFont(normalFont);
        mLabel lAltt=new mLabel("Alt:");
        lAltc.backGround = Color.White;
        lAltc.setFont(normalFont);
        mLabel lSpeedt=new mLabel("Spd");
        lSpeedc.backGround = Color.White;
        lSpeedc.setFont(boldFont);
        mLabel lHeadingt=new mLabel("Hdg");
        lHeadingc.backGround = Color.White;
        lHeadingc.setFont(boldFont);
        mLabel lTimet= new mLabel("Time:");
        lTimet.setFont(boldFont);
        lTimec.backGround = Color.White;
        lTimec.setFont(largeFont);
        mLabel lStatust=new mLabel("Status:");
        lStatust.setFont(boldFont);
        mLabel lLent= new mLabel("Dist:");
        lLent.setFont(boldFont);
        lLenc.backGround = Color.White;
        lLenc.setFont(largeFont);
        mLabel lUpt=new mLabel("U");
        mLabel lDwt=new mLabel("D");
        mLabel lAvt=new mLabel("Avg :");
        mLabel lInt=new mLabel("Inst:");
        lAvSpeedc.backGround = Color.White;
        lAvSpeedc.setFont(normalFont);
        lAvHeadingc.backGround = Color.White;
        lAvHeadingc.setFont(normalFont);
        
        CellPanel cPanelGPS = new CellPanel();
        // cPanelGPS.setBorder(BDR_OUTLINE|BF_RECT,2);
        CellPanel cPanelAlt = new CellPanel();
        cPanelAlt.setBorder(BDR_OUTLINE|BF_RECT,1);
        CellPanel cPanelSpdHdg = new CellPanel();
        cPanelSpdHdg.setBorder(BDR_OUTLINE|BF_RECT,1);
        CellPanel cPanelTT = new CellPanel();
        cPanelTT.setBorder(BDR_OUTLINE|BF_RECT,1);
        
        //lLatt.setPreferredSize(dScr.width/3,lLatt.getFont().getSize()+2);
        cPanelGPS.addNext(lLatt,HCONTRACT, SHRINK);
        cPanelGPS.addNext(lLatc,HCONTRACT, SHRINK);
        //bOpt.setPreferredSize(dScr.width/32, (dScr.height*3)/16);
        cPanelGPS.addLast(bGps,STRETCH, FILL).setTag(SPAN,new Dimension(1,3));
        cPanelGPS.addNext(lLont,HCONTRACT, SHRINK);
        cPanelGPS.addLast(lLonc,HCONTRACT, SHRINK);
        cPanelGPS.addNext(lUTCt,HCONTRACT, SHRINK);
        cPanelGPS.addLast(lUTCc,HCONTRACT, SHRINK);
        
        cPanelGPS.setPreferredSize(dScr.width,(int) (dScr.height*1.5)/16);
        addLast(cPanelGPS);
        
        /* In case a control is needed to display on the WM screen
        Control logControl = logger.getLogControl();
        logControl.resizeTo(dScr.width, dScr.height/16);
        addLast(logControl); */
        
        cPanelAlt.addNext(lAltt,HGROW|VCONTRACT,HCENTER);
        cPanelAlt.addNext(lUpt,HGROW|VCONTRACT,VCENTER);
        cPanelAlt.addLast(lUpc,FILL,VCENTER);
        cPanelAlt.addNext(lAltc,FILL,VCENTER);
        cPanelAlt.addNext(lDwt,HGROW|VCONTRACT,VCENTER);
        cPanelAlt.addLast(lDwc,FILL,VCENTER);
        cPanelAlt.setPreferredSize(dScr.width, (dScr.height/16));
        addLast(cPanelAlt);
        
        cPanelSpdHdg.addNext(new mLabel(""));
        cPanelSpdHdg.addNext(lSpeedt, HGROW, HCENTER);
        cPanelSpdHdg.addNext(new mLabel(""));
        cPanelSpdHdg.addLast(lHeadingt, HGROW, HCENTER);
        cPanelSpdHdg.addNext(lInt);
        cPanelSpdHdg.addNext(lSpeedc, HGROW, HEXPAND);
        cPanelSpdHdg.addNext(new mLabel(""));
        cPanelSpdHdg.addLast(lHeadingc, HGROW, HEXPAND);
        cPanelSpdHdg.addNext(lAvt);
        cPanelSpdHdg.addNext(lAvSpeedc, HGROW, HEXPAND);
        cPanelSpdHdg.addNext(new mLabel(""));
        cPanelSpdHdg.addLast(lAvHeadingc, CENTER, HEXPAND|HCENTER);
        cPanelSpdHdg.setPreferredSize(dScr.width, (int) ((dScr.height*1.5)/16));
        addLast(cPanelSpdHdg);
        
        cPanelTT.addNext(lTimet,DONTSTRETCH,VCENTER);
        cPanelTT.addLast(lTimec,FILL, VCENTER);
        cPanelTT.addNext(lLent,DONTSTRETCH, VCENTER);
        cPanelTT.addLast(lLenc,FILL, VCENTER);
        cPanelTT.addNext(lStatust,DONTSTRETCH,VCENTER);
        cPanelTT.addLast(bStatusc,FILL,VCENTER);
        cPanelTT.setPreferredSize(dScr.width, (dScr.height*2)/16);
        addLast(cPanelTT);
        
        MenuItem miSeparator=new MenuItem("-");
        Menu menuFileActions;
        /** menu for actions */
        MenuItem miOptions=new MenuItem("Options$O","ewe/optionssmall.bmp",Color.White);
        miOptions.action="P";
        MenuItem miCalibrate=new MenuItem("Calibrate$C","res/iconcalibsmall.png",Color.White);
        miCalibrate.action="C";
        MenuItem miNavigate=new MenuItem("Navigate$N","res/iconnavigate.png",Color.White);
        miNavigate.action="N";
        MenuItem miConvert=new MenuItem("Convert$v","res/convertsmall.png",Color.White);
        miConvert.action="V";
        MenuItem miExit=new MenuItem("Exit$x","ewe/exitsmall.bmp",Color.White);
        miExit.action="X";
        MenuItem[] tmiFileActions=new MenuItem[]{miNavigate,miOptions,
        miCalibrate,miConvert,miSeparator,miExit};
        
        menuFileActions=new Menu(tmiFileActions,"GO TO");
        bGoto=new PullDownForKey("GO TO",menuFileActions);
        ActionKeysDispatcher akd=new ActionKeysDispatcher();
        bGoto.setActionKeyListener(akd);
        
        miRecStop.action="R";
        miHold.action="H";
        miPause.action="A";
        MenuItem[] tmiRec=new MenuItem[]{miHold,miPause,miRecStop};
        
        Menu menuRec=new Menu(tmiRec,"RECORD");
        pdRecord=new PullDownForKey("Record",menuRec);
        pdRecord.setActionKeyListener(akd);
        
        akd.setLeftAndRight(bGoto,pdRecord);
        
        // Track key events to the dispatcher to get presses
        bGps.setMyActioKeyDispatcher(akd);
        bStatusc.setMyActioKeyDispatcher(akd);
        
        courseLogAppFormSetup(bGoto,pdRecord,null);
        
        updateButtons();
        Vm.setSIP(0,this.getWindow());
        Gui.takeFocus(pdRecord,Control.ByRequest);
    }
    
    // CLASS METHODS
    
    /**
     * Update the display according to the position tracer status
     * @param p The monitored PositionTracer.
     */
    public void updateTracerStatus(PositionTracer p) {
        int gpsStatus=p.getGPSStatus();
        if(gpsStatus!=myLastGPSStatus) {
            switch(gpsStatus) {
                case SerialGPSConnection.STATUS_OK:
                    bGps.backGround=MainApplication.NORMALCOLOR;
                    myLastTracer=p;
                    break;
                case SerialGPSConnection.STATUS_NOK:
                    bGps.backGround=MainApplication.BADCOLOR;
                    myLastTracer=null;
                    break;
                case SerialGPSConnection.STATUS_GPSLOST:
                    bGps.backGround=MainApplication.LOSTCOLOR;
                    break;
            }
            bGps.repaint();
            myLastGPSStatus=gpsStatus;
        }
        if(gpsStatus!=SerialGPSConnection.STATUS_NOK) {
            lUTCc.setText(Converters.formatUTC(p.myGPS.currentPoint.time));
        }
        TrackPoint tP=p.myCurrent;
        if(tP.lat!=GeoPoint.dummyAng) {
//        if(gpsStatus==SerialGPSConnection.STATUS_OK) {
            lLatc.setText(Converters.ddmmss(tP.lat,Converters.DDMMSS_LAT));
            lLonc.setText(Converters.ddmmss(tP.lon,Converters.DDMMSS_LON));
            lAltc.setText(Converters.formatAltitude(tP.altSeaLevel));
            lDwc.setText(Converters.formatAltitude(tP.downwardsHere));
            lHeadingc.setText(Converters.formatHeading(tP.heading));
            lSpeedc.setText(Converters.formatSpeed(tP.speed));
            lLenc.setText(Converters.formatDistance(tP.lengthHere));
            lTimec.setText(Converters.formatDuration(tP.durationHere));
            lUpc.setText(Converters.formatAltitude(tP.upwardsHere));
            lAvHeadingc.setText(Converters.formatHeading(tP.avgHeading));
            lAvSpeedc.setText(Converters.formatSpeed(tP.avgSpeed));
        }
        
        if(myLastTracerState!=p.myStatus) {
            myLastTracerState=p.myStatus;
            updateButtons();
        }
    }
    
    /**
     * Update tracer display labels.
     */
    public void updateTrackData(PositionTracer p) {
        if(p.myGPS.status==SerialGPSConnection.STATUS_OK) {
            TrackPoint tP=p.myCurrent;
            lAvHeadingc.setText(Converters.formatHeading(tP.avgHeading));
            lAvSpeedc.setText(Converters.formatSpeed(tP.avgSpeed));
        }
    }
    
    /**
     * Update buttons according to tracer status.
     */
    public void updateButtons() {
        if(myLastTracer!=null) {
            myLastTracerState=myLastTracer.myStatus;
        }
        if(myLastTracerState==PositionTracer.STATUS_RECORDING) {
            miRecStop.iconize(iconStop.text,iconStop.icon,true);
            miRecStop.action="R";
            miHold.modifiers &= ~MenuItem.Disabled;
            miPause.modifiers &= ~MenuItem.Disabled;
            bStatusc.image=iconRec;
        }else{
            miRecStop.iconize(iconRec.text,iconRec.icon,true);
            miRecStop.action="R";
            miHold.modifiers |=MenuItem.Disabled;
            miPause.modifiers |=MenuItem.Disabled;
            if(myLastTracerState==PositionTracer.STATUS_OFF) {
                bStatusc.image=iconStop;
            }else if(myLastTracerState==PositionTracer.STATUS_HOLD) {
                bStatusc.image=iconHold;
            }else if(myLastTracerState==PositionTracer.STATUS_PAUSED){
                bStatusc.image=iconPause;
            }
        }
        bStatusc.repaint();
    }
    
    /** Triggers an exit from the application */
    public void tryExitForm(int nextState) {
        myApp.myNextActivePanel=nextState; // Specify next to the application
        myIsActive=false;
        exit(Form.IDOK);
    }
    
    /**
     * Start recording the track.
     */
    public void tryRecordOrStop() {
        if(myLastTracer!=null) {
            if(myLastTracer.myStatus==PositionTracer.STATUS_RECORDING) {
                myLastTracer.setStatus(PositionTracer.STATUS_OFF);
            }else {
                myLastTracer.setStatus(PositionTracer.STATUS_RECORDING);
            }
            updateButtons();
        }
    }
    
    /**
     * Hold recording the track.
     */
    public void tryHold() {
        if(myLastTracer!=null) {
            myLastTracer.setStatus(PositionTracer.STATUS_HOLD);
            updateButtons();
        }
    }
    
    /**
     * Pause recording the track.
     */
    public void tryPause() {
        if(myLastTracer!=null) {
            myLastTracer.setStatus(PositionTracer.STATUS_PAUSED);
            updateButtons();
        }
    }
    
// OVERIDDEN METHODS
    
    public void onEvent(Event ev) {
        try {
            if((!myIsActive)&&(ev.type==ControlEvent.FOCUS_IN)) {
                // I get focus in events so I am active!
                // The target is an mButton I don't know
                myIsActive=true;
            }
            
            if((ev.target==this)&&(ev.type==ControlEvent.FOCUS_IN)) {
//            Gui.takeFocus(defC,ControlConstants.ByRequest);
                tryExitForm(-1);
//            ev.consumed=true;
            }
            
            if((ev.target == bGoto)||(ev.target==pdRecord)) {
                if(ev.type==MenuEvent.SELECTED) {
//                CourseLogAppForm.dbgL.addToLog("\n pressed:"+ev.target+" item "+pdFileActions.getLastSelected());
                    MenuEvent mEv=(MenuEvent) ev;
                    final MenuItem selItem= (MenuItem) mEv.selectedItem;
//                CourseLogAppForm.dbgL.addToLog("\n selected:"+selItem.action);
                    mThread actThread= new mThread(new Runnable() {
                        public void run() {
                            executeAction(selItem.action);
                            //updateFormState();
                        }
                    });
                    actThread.start();
                    ev.consumed=true;
                }
            }
            super.onEvent(ev);
        } catch (NullPointerException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (IndexOutOfBoundsException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (ClassCastException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        }
    }
    
    public void executeAction(String actString) {
        if(actString.equals("X")) tryExitForm(-1);
        else if(actString.equals("C")) tryExitForm(MainApplication.ACTIVECALIBRATION);
        else if(actString.equals("V")) tryExitForm(MainApplication.ACTIVECONVERT);
        else if(actString.equals("N")) tryExitForm(MainApplication.ACTIVENAVIGATE);
        else if(actString.equals("P")) tryExitForm(MainApplication.ACTIVEOPTIONS);
        else if(actString.equals("R")) tryRecordOrStop();
        else if(actString.equals("H")) tryHold();
        else if(actString.equals("A")) tryPause();
    }
    
// INTERFACE METHODS
    
    /**
     * Specify that this displayer is active.
     * @return true if the display is on, false otherwise.
     */
    public boolean isActive() {
        return myIsActive;
    }
    
    /**
     * Called when the PositionTracer changes.
     * @see courselog.gpsutils.XPositionDisplayer
     * @param opt option indicating if status and/or trakc changed
     * @param pTr Tracer which has been updated.
     */
    public void listenerUpdated(int opt, PositionTracer pTr) {
        if((opt&PositionTracer.OPT_STATUSCHANGED)!=0) {
            updateTracerStatus(pTr);
        }
        if((opt&PositionTracer.OPT_TRACECHANGED)!=0) {
            updateTrackData(pTr);
        }
    }
    
    public void setCurrentTrack(TrackRecord tr) {
    }
    
}
